查看原文
其他

安卓逆向:这是一篇逆向基础ARM32指令集的总结

游戏安全攻防 小道安全 2022-11-13
这是一篇关于ARM32指令集的总结文章,后续会不断输出一系列逆向分析破解相关的文章。


ARM微处理器共有37个32位寄存器,其中31个为通用寄存器,6个为状态寄存器。但是这些寄存器不能被同时访问,具体哪些寄存器是可以访问的,取决ARM处理器的工作状态及具体的运行模式。但在任何时候,通用寄存器R14~R0、程序计数器PC、一个状态寄存器都是可访问的。
            未分组寄存器 R0 ~ R7,共8个;
            分组寄存器 R8 ~ R12R13 ~ R14
             R8 ~ R12:其中FIQ模式下有单独的一组 R8 ~ R12,共5个;另外 6种模式共用一组R8 ~ R12,共5个;总共10个;
            R13 ~ R14:其中USR和SYS模式(表格的第一列)共用一组R13 ~ R14共2个,另外5种模式下各有独自的一组R13 ~ R14共10个;总 共12个;
            程序计数器 PC 即R15,共1个;
      分组寄存器R13、R14
            1、寄存器R13通常做堆栈指针SP
          2、寄存器R14用作子程序链接寄存器(Link Register-LR),也称为LR,指向函数的返回地址。
上图中其中模式的详解
usr(用户模式):ARM处理器正常程序执行模式。
fiq(快速中断模式):用于高速数据传输或通道处理
irq(外部中断模式):用于通用的中断处理
svc(管理模式):操作系统使用的保护模式
abt (数据访问终止模式):当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护。
sys(系统模式):运行具有特权的操作系统任务。
und(未定义指令中止模式):当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真。


ARM指令集是指计算机ARM操作指令系统。ARM指令集可以分为跳转指令、数据处理指令、程序状态寄存器(PSR)处理指令、加载/存储指令、协处理器指令和异常产生指令六大类。

(注意:ARM指令不区分大小写,可以分别大小写表示,不能大小写混用)

1.数据处理指令
  1.1数据传输指令有两个:mov,mvn    
      MOV R2,R1   ;将寄存器R1的值传送到寄存器R2
      MOV PC,R14   ;将寄存器R14的值传送到PC,常用于子程序返回
     MVN R0,#0   ;将立即数0取反传送到寄存器R0中,完成后R0=-1                                         (有 符号位取反)
   1.2算术指令有六个:ADD、SUB、RSB、ADC、SBC、RSC
       ADD:加法汇编指令
          ADD  R0,R1,R2      ;R0 = R1 + R2
          ADD  R0,R1,#256    ;R0 = R1 + 256
          ADD  R0,R2,R3,LSL#1    ;R0 = R2 + (R3 << 1)
      SUB:减法汇编指令
        SUB  R0,R1,R2         ;R0 = R1 - R2
        SUB  R0,R1,#256     ;R0 = R1 - 256
       SUB  R0,R2,R3,LSL#1      ;R0 = R2 - (R3 << 1)
     RSB:逆向减法指令
       RSB r1, r0, #5  ;r1 = 5 - r0
       RSB r0, r1, r2    ;r0 = r2-r1
   ADC:带进位的加法指令
       ADC r5, r1, r3  ; r5 =r1 + r3 + 'C' 其中'C'位是CPSR 进位标志     
    SBC:带借位的减法指令
        SBC r5, r1, r3  ; r5 =r1 - r3 - 'C' 其中'C'位是CPSR 进位标志   
      RSC:带进位的翻转减指令
        RSC,r0,r1,r2 ;r0=r2-r1 - !'C' 其中!'C' CPSR中C条件标志位的反码
  1.3逻辑指令有四个:AND、OFF、EOR、BIC
       AND:用于两个操作数上进行逻辑与运算,并把结果放置目的寄存器中
           AND  R0,R0,#3     ;该指令保持R0的0、1位,其余位清零。
       ORR:用于在两个操作数上进行逻辑或运算,并把结果放置到目的寄存器中。
          ORR  R0,R0,#3      ;该指令设置R0的0、1位,其余位保持不变。
      EOR:用于在两个操作数上进行逻辑异或运算,并把结果放置到目的寄存器中。
          EOR  R0,R0,#3    ;该指令反转R0的0、1位,其余位保持不变。
      BIC:用于清除操作数1的某位数,并将结果放置到目的寄存器
         BIC  R0,R0,#%1011  ;该指令清除 R0 中的位 0、1、和 3,其余 的位保持不变。
   1.4比较指令有四个:CMP、CMN、TST、TEQ
      CMP(直接比较指令)指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行比较,同时更新CPSR中条件标志位的值。
            CMP R1,R0   ;将寄存器R1的值与寄存器R0的值相减,并根据结果设置CPSR的标志位
            CMP R1,#100  ;将寄存器R1的值与立即数100相减,并根据结果设置CPSR的标志位
       CMN(负数比较指令)指令用于把一个寄存器的内容和另一个寄存器的内容或立即数取反后进行比较,同时更新CPSR中条件标志位的值。
       TST(位测试指令) 来检查是否设置了特定的位。操作数 1 是要测试的数据字而操作数 2 是一个位掩码,TST指令将操作数1与操作数2做逻 辑与运算,和ANDS的区别就是不保存结果。TST会改变CPSR的条件标志位。
      TEQ(相等测试指令)指令用于把一个寄存器Rn的内容和另一个操作数2按位进行异或运算,并根据运算结果更新CPSR中条件标志位的值。
1.5乘法指令有六个:MUL、MLA、UMULL、UMLAL、SMULL、SMLAL
以下是具体指令的用法解释: 
     MUL R0,R1 ,R2 ;R0= R1*R2

     MULA R0,R1,R2,R3;  R0 =R1*R2+R3

     UMULL R0, R1,R2,R3; R0=(R2*R3)的低32位,R1=(R2*R3)的高32位

     UMLAL R0,R1,R2,R3;R0=(R2*R3)的低32位+R0,R1=(R2*R3)的高32位+R1

    SMULL,R0,R1,R2,R3;R0=(R2*R3)的低32位,R1=(R2*R3)的高32位

   SMLAL  R0,R1,R2,R3;R0=(R2*R3)的低32位+R0,R1= (R2*R3)的高32位+R1

  1.6前导零计数指令有1个:CLZ
        CLZ:零计数指令
2.跳转指令也叫转移指令 
在ARM程序中有两种方法可以实现程序流程的跳转:
   1.使用专门的跳转指令。
   2.直接向程序计数器PC写入跳转地址值。
通过向程序计数器PC写入跳转地址值,可以实现在4GB的地址空间中的任意跳转,在跳转之前结合使用MOV LR,PC等类似指令,可以保存将来的返回地址值,从而实现在4GB连续的线性地址空间的子程序调用。
ARM指令集中的跳转指令可以完成从当前指令向前或向后的32MB的地址空间的跳转,包括以下4条指令:
  2.1 B 跳转指令
  2.2 BL 带返回的跳转指令
  2.3 BLX 带返回和状态切换的跳转指令
  2.4BX 带状态切换的跳转指令
3.程序状态寄存器访问指令
  状态寄存器有两个:MRS、MSR
    MRS指令用于将程序状态寄存器的内容传送到通用寄存器中。
        MRS R0,CPSR   ;传送CPSR的内容到R0
        MRS R0,SPSR   ;传送SPSR的内容到R0

    MSR指令用于将操作数的内容传送到程序状态寄存器的特定域中
        MSR CPSR,R0   ;传送R0的内容到CPSR
        MSR SPSR,R0   ;传送R0的内容到SPSR
        MSR CPSR_c,R0  ;传送R0的内容到SPSR,但仅仅修改CPSR中的控制位
  4.加载存储指令
     4.1加载指令有四个:LDR、LDRB、LDRH、LDM
     4.2存储指令有四个:STR、STRB、STRH、STM
    LDR指令用于从存储器中将一个32位的字数据传送到目的寄存器中。
       LDR  R0,[R1]             ;将存储器地址为R1的字数据读入寄存器R0。
      LDR  R0,[R1,R2]  ;将存储器地址为R1+R2的字数据读入寄存器R0。
      LDR  R0,[R1,#8] ;将存储器地址为R1+8的字数据读入寄存器R0。
      LDR  R0,[R1,R2] !     ;将存储器地址为R1+R2的字数据读入寄存器R0,并将新地址R1+R2写入R1。
      LDR  R0,[R1,#8] !       ;将存储器地址为R1+8的字数据读入寄存器R0,并将新地址R1+8写入R1。
      LDR  R0,[R1],R2        ;将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2写入R1。
       LDR  R0,[R1,R2,LSL#2]!;将存储器地址为R1+R2×4的字数据读入寄存器R0,并将新地址R1+R2×4写入R1。
       LDR  R0,[R1],R2,LSL#2  ;将存储器地址为R1的字数据读入寄存器 R0,并将新地址R1+R2×4写入R1

     LDRB指令用于从存储器中将一个8位的字节数据传送到目的寄存器中,同时将寄存器的高24位清零。
       LDRB  R0,[R1]      ;将存储器地址为R1的字节数据读入寄存器R0,并将R0的高24位清零。
       LDRB  R0,[R1,#8]     ;将存储器地址为R1+8的字节数据读入寄存器R0,并将R0的高24位清零。

       LDRH指令用于从存储器中将一个16位的半字数据传送到目的寄存器中,同时将寄存器的高16位清零。
      LDRH  R0,[R1]      ;将存储器地址为R1的半字数据读入寄存器R0,并将R0的高16位清零。
      LDRH  R0,[R1,#8]     ;将存储器地址为R1+8的半字数据读入寄存器 R0,并将R0的高16位清零。
      LDRH  R0,[R1,R2]     ;将存储器地址为R1+R2的半字数据读入寄存器R0,并将R0的高16位清零。

     STR指令用于从源寄存器中将一个32位的字数据传送到存储器中。
         STR R0,[R1],#8 ;将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1。
        STR R0,[R1,#8] ;将R0中的字数据写入以R1+8为地址的存储器中。

    STRB指令用于从源寄存器中将一个8位的字节数据传送到存储器中。
    STRB  R0,[R1];将寄存器R0中的字节数据写入以R1为地址的存储器中。
    STRB  R0,[R1,#8]     ;将寄存器R0中的字节数据写入以R1+8为地址的存储器中。

     STRH指令用于从源寄存器中将一个16位的半字数据传送到存储器中。
            STRH  R0,[R1]      ;将寄存器R0中的半字数据写入以R1为地址的存储器中。
      STRH  R0,[R1,#8]     ;将寄存器R0中的半字数据写入以R1+8为地址的存储器中。

   LDM  批量数据加载指令
   STM  批量数据存储指令
        常见后缀
        IA 每次传送后地址加1;
        IB 每次传送前地址加1;
        DA 每次传送后地址减1;
        DB 每次传送前地址减1;
        FD 满递减堆栈;
        ED 空递减堆栈;
        FA 满递增堆栈;
        EA 空递增堆栈;

    STMFD  R13!,{R0,R4-R12,LR}  ;将寄存器列表中的寄存器(R0,R4 到R12,LR)存入堆栈。

    LDMFD  R13!,{R0,R4-R12,PC}  ;将堆栈内容恢复到寄存器(R0,R4到R12,LR)。
 
5.异常产生指令
   异常产生指令有两个:SWI、KPT
       SWI指令用于产生软件中断,以便用户程序能调用操作系统的系统例程。
             SWI  0x02      ;该指令调用操作系统编号位02的系统例程。

        BKPT指令产生软件断点中断,可用于程序的调试。

6.伪指令:
  伪指令主要有四大种类:符号定义伪指令,数据定义伪指令,汇编控制伪指令,其他常用伪指令

     
6.1符号定义伪指令:
用于定义全局变量的:GBLA、GBLL、GBLS
用于定义局部变量的:LCLA、LCLL、LCLS
用于对变量赋值的:SETA、SETL、SETS
位通用寄存器列表定义名称的RLIST
GBLA:用于定义一个全局的数字变量,并将其初始化为0
GBLL:用于定义全局的逻辑变量,并将其初始化为假
GBLS:用于定义全局的字符串变量,并将其初始化为空。
LCLA:用于定义一个全局的数字变量,并将其初始化为0
LCLL:用于定义全局的逻辑变量,并将其初始化为假
LCLS:用于定义全局的字符串变量,并将其初始化为空。
SETA:用于给一个数字变量赋值
SETL:用于给一个逻辑变量赋值
SETS:用于给字符串变量赋值。

6.2数据定义伪指令:
DCB:用于分配一片连续的字节存储单元并用指定的数据初始化
DCW(DCWU):用于分配一片连续的半字节存储单元并用指定数据初始化。
DCD(DCDU):用于分配一片连续的字节存储单元并用指定数据初始化。
DCFD(DCFDU):用于为双精度的浮点数分配一片连续的字节存储单元并用指定数据初始化。
DCFS(DCFSU):用于为单精度的浮点数分配一片连续的字节存储单元并用指定数据初始化。
DCQ(DCQU):用于分配一片以8字节为单位的连续的字节存储单元并用指定数据初始化。
SPACE:用于分配一片连续的存储单元
MAP用于定义一个结构体的内存表首地址
FIFLD:用于定义一个结构体的内存表数据域

6.3汇编控制伪指令:
IF,ELSE,ENDIF
WHILE,WEND
MACRO,MEND
NEXIT

6.4其他常用伪指令:
AREA:用于定义一个代码段或数据域。
ROUT:用于给一个局部变量定义作用范围。
EQU:用于为程序中的常量,标号等定义一个等效的字符名称类似于define

ARM常见指令集速记
ARM条件码速记


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存